#include <stdio.h>
#include <cassert>
#include <utility>
#include <string.h>
#include <inttypes.h>

using namespace std;
#include "cbp3_def.h"
#include "cbp3_framework.h"

#include <map>

#ifndef INIT_VALUE
#define INIT_VALUE 0
#endif


struct my_int {
  int value;
  my_int(): value(INIT_VALUE){}
};


// local history based predictor, infinite memory

map< uint32_t, uint32_t > pc_to_history_fetch;
map< uint32_t, uint32_t > pc_to_history_retire;

map< pair<uint32_t , uint32_t> , my_int > history_to_counter;

map< pair<uint32_t , uint32_t> , uint32_t > correlation;
uint32_t correlation_counter;

void PredictorInit() {
    PredictorReset();
}

void PredictorReset() {
    pc_to_history_fetch.clear();

    pc_to_history_retire.clear();
 
    history_to_counter.clear();

    correlation_counter = 0;
    correlation.clear();
    //printf("Predictor: last value (local predictor), Cost: INFINITE memory\n");
}

void PredictorRunACycle() {
    // get info about what uops are processed at each pipeline stage
    const cbp3_cycle_activity_t *cycle_info = get_cycle_info();

    // make prediction at fetch stage
    for (int i = 0; i < cycle_info->num_fetch; i++) {
        uint32_t fe_ptr = cycle_info->fetch_q[i];
        const cbp3_uop_dynamic_t *uop = &fetch_entry(fe_ptr)->uop;

        if ( uop->type & IS_BR_CONDITIONAL ) {
	  //Get appropriate history:
	  uint32_t h = pc_to_history_fetch[uop->pc];

	  //Get prediction based on history:
	  pair<uint32_t , uint32_t> key(h,uop->pc);
	  bool pred = (history_to_counter[key].value >= 0);

	  if( correlation[key] == 0 ){
		++correlation_counter;
		correlation[key] = correlation_counter;
	  }

          assert(report_pred(fe_ptr, false, pred, correlation[key]));
	  //pair<uint32_t , uint32_t> key(h,uop->pc);
	  bool t = uop->br_taken;
	  if (t && (history_to_counter[key].value < 1))
	        (history_to_counter[key].value) ++;
            else if (!t && (history_to_counter[key].value > -2))
                (history_to_counter[key].value) --;
        }

	if (uop->type & IS_BR_CONDITIONAL)
            pc_to_history_fetch[uop->pc] = (pc_to_history_fetch[uop->pc] << 1) | (uop->br_taken ? 1 : 0);
        else if (uop_is_branch(uop->type))
            pc_to_history_fetch[uop->pc] = (pc_to_history_fetch[uop->pc] << 1) | 1;

 }

    for (int i = 0; i < cycle_info->num_retire; i++) {
        uint32_t rob_ptr = cycle_info->retire_q[i];
        const cbp3_uop_dynamic_t *uop = &rob_entry(rob_ptr)->uop;

        if ( uop->type & IS_BR_CONDITIONAL) {

	  //Update predictor:
	  //uint32_t h = pc_to_history_retire[uop->pc];


        }
	if (uop->type & IS_BR_CONDITIONAL)
            pc_to_history_retire[uop->pc] = (pc_to_history_retire[uop->pc] << 1) | (uop->br_taken ? 1 : 0);
        else if (uop_is_branch(uop->type))
            pc_to_history_retire[uop->pc] = (pc_to_history_retire[uop->pc] << 1) | 1;
    }
}

void PredictorRunEnd() {
    // nothing
}

void PredictorExit() {
    // nothing
}
